package proj2501.simple_module;

import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JRootPane;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities;

/** An utility program for formatting HTML text for Google Sites. */
class P003GoogleSitesFormatter extends JFrame {
	private SourceTextArea source;
	private DestinationTextArea destination;
	
	public P003GoogleSitesFormatter() {
//		setDefaultCloseOperation(DISPOSE_ON_CLOSE);
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		setSize(800, 600);
		setTitle("Google Sites Formatter");
		
		JRootPane rootPane = getRootPane();
		InputMap inputMap = rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
		ActionMap actionMap = rootPane.getActionMap();
		inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "Escape");
		actionMap.put("Escape", new AbstractAction() {
			@Override
			public void actionPerformed(ActionEvent e) {
				P003GoogleSitesFormatter frame = P003GoogleSitesFormatter.this;
				frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING));
			}
		});

		inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.CTRL_DOWN_MASK), "Ctrl+Enter");
		actionMap.put("Ctrl+Enter", new AbstractAction() {
			@Override
			public void actionPerformed(ActionEvent e) {
				System.out.println("Source - SelectionStart: " + source.getSelectionStart());
				System.out.println("Source - SelectionEnd: " + source.getSelectionEnd());
				format();
			}
		});
		
		addWindowListener(new WindowAdapter() {
			@Override
			public void windowActivated(WindowEvent e) {
				super.windowActivated(e);
				System.out.println("Window activated.");
				source.setText("");
				source.paste();
				source.select(0, 0);
				String s = source.getText();
				System.out.println("Pasted a text from the clipboard. Num chars: " + s.length());
				SwingUtilities.invokeLater(new Runnable() {
					@Override
					public void run() {
						format();
						destination.selectAll();
						destination.copy();
						System.out.println("Copied the text into the clipboard. Num chars: " + destination.getText().length());
					}
				});
			}
			@Override
			public void windowClosing(WindowEvent e) {
				System.out.println("Closing window. (Exiting program.)");
				super.windowClosing(e);
			}
		});

		setLayout(new BorderLayout());
		JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true);
		splitPane.setResizeWeight(0.5);
		JScrollPane leftScrollPane = new JScrollPane();
		leftScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
		source = new SourceTextArea();
		leftScrollPane.setViewportView(source);

		JScrollPane rightScrollPane = new JScrollPane();
		rightScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
		destination = new DestinationTextArea();
		rightScrollPane.setViewportView(destination);
		
		splitPane.setLeftComponent(leftScrollPane);
		splitPane.setRightComponent(rightScrollPane);
		add(splitPane);
		
		setVisible(true);
	}
	
	private void format() {
		System.out.println("Formatting the text.");
		final String text = source.getText();
		int numBefore = text.length();
		
		Pattern pattern = Pattern.compile("\n");
		Matcher matcher = pattern.matcher(text);
		int numN = 0;
		while (matcher.find()) {
			numN++;
		}
		System.out.println("Number of \"\\n\": " + numN);
		
		pattern = Pattern.compile("\r\n");
		matcher = pattern.matcher(text);
		int numRN = 0;
		while (matcher.find()) {
			numRN++;
		}
		System.out.println("Number of \"\\r\\n\": " + numRN);
		
		pattern = Pattern.compile("\r");
		matcher = pattern.matcher(text);
		int numR = 0;
		while (matcher.find()) {
			numR++;
		}
		System.out.println("Number of \"\\r\": " + numR);
		
		pattern = Pattern.compile("<br>");
		matcher = pattern.matcher(text);
		int numBR = 0;
		while (matcher.find()) {
			numBR++;
		}
		System.out.println("Number of \"<br>\": " + numBR);
//		text = text.replaceAll("<br>", "");
		
		String text2 = "";
		int p1 = 0;
		int numSpaces = 0;
		int numTrailingSpaces = 0;
		int depth = 0;
		int numBreakRemoved = 0;
		while (true) {
			final int p0 = p1;
			if (text.length() <= p1) {
				break;
			}
			
			// Ignores a code block.
			if (text.startsWith("<code>", p1)) {
				final int blockStart = p1;
				while (true) {
					if (text.startsWith("</code>", p1)) {
						text2 += "</code>";
						p1 += 7;
						final int blockEnd = p1;
						System.out.println("Code block ignored. (from " + blockStart + " to " + blockEnd + ")");
						break;
					}
					text2 += text.substring(p1, p1 + 1);
					p1++;
				}
			}
			
			if (text.startsWith("t", p1)) {
				text2 += "t";
				p1++;
			} else if (text.startsWith("<br>", p1)) {
				p1 += 4;
				numBreakRemoved++;
			} else if (text.startsWith("<p>\n</p>", p1)) {
				p1 += 8;
				System.out.println("Removed a blank \"p\" element.");
			} else if (text.startsWith("\n</p>", p1)) {
				p1 += 1;
			} else if (text.startsWith("</p>", p1)) {
				text2 += "</p>\n\n";
				p1 += 4;
			} else if (text.startsWith("<h1>", p1)) {
				text2 += "\n<h1>";
				p1 += 4;
			} else if (text.startsWith("\n</h1>", p1)) {
				p1 += 1;
			} else if (text.startsWith("</h1>", p1)) {
				text2 += "</h1>\n\n";
				p1 += 5;
			} else if (text.startsWith("<h2>", p1)) {
				text2 += "\n<h2>";
				p1 += 4;
			} else if (text.startsWith("\n</h2>", p1)) {
				p1 += 1;
			} else if (text.startsWith("</h2>", p1)) {
				text2 += "</h2>\n\n";
				p1 += 5;
			} else if (text.startsWith("<h3>", p1)) {
				text2 += "\n<h3>";
				p1 += 4;
			} else if (text.startsWith("\n</h3>", p1)) {
				p1 += 1;
			} else if (text.startsWith("</h3>", p1)) {
				text2 += "</h3>\n\n";
				p1 += 5;
			} else if (text.startsWith("<h4>", p1)) {
				text2 += "\n<h4>";
				p1 += 4;
			} else if (text.startsWith("\n</h4>", p1)) {
				p1 += 1;
			} else if (text.startsWith("</h4>", p1)) {
				text2 += "</h4>\n\n";
				p1 += 5;
			} else if (text.startsWith("<h5>", p1)) {
				text2 += "\n<h4>";
				p1 += 4;
			} else if (text.startsWith("\n</h5>", p1)) {
				p1 += 1;
			} else if (text.startsWith("</h5>", p1)) {
				text2 += "</h5>\n\n";
				p1 += 5;
			} else if (text.startsWith("<h6>", p1)) {
				text2 += "\n<h6>";
				p1 += 4;
			} else if (text.startsWith("\n</h6>", p1)) {
				p1 += 1;
			} else if (text.startsWith("</h6>", p1)) {
				text2 += "</h6>\n\n";
				p1 += 5;
			} else if (text.startsWith("<ul>\n</ul>", p1)) {
				p1 += 10;
				System.out.println("Removed a blank \"ul\" element.");
			} else if (text.startsWith("<ul>\n<ul>\n </ul>\n</ul>", p1)) {
				p1 += 22;
				System.out.println("Removed a blank \"ul\" element. (<ul>\\n<ul>\\n </ul>\\n</ul>)");
			} else if (text.startsWith("<ul><", p1)) {
//				text2 = text2.substring(0, text2.length() - numSpaces);
//				for (int i = 0; i < depth; i++) {
//					text2 += " ";
//				}
				text2 += "<ul>\n";
				p1 += 4;
				depth ++;
			} else if (text.startsWith("<ul>", p1)) {
//				text2 = text2.substring(0, text2.length() - numSpaces);
//				for (int i = 0; i < depth; i++) {
//					text2 += " ";
//				}
				text2 += "<ul>";
				p1 += 4;
				depth ++;
			} else if (text.startsWith("</ul>", p1)) {
				if (text2.endsWith(">")) {
					text2 += "\n";
				}
				
				depth --;
				text2 = text2.substring(0, text2.length() - numSpaces);
				for (int i = 0; i < depth; i++) {
					text2 += " ";
				}
				text2 += "</ul>";
				if (depth == 0) {
					text2 += "\n";
				}
				p1 += 5;
			} else if (text.startsWith("<ol>\n</ol>", p1)) {
				p1 += 10;
				System.out.println("Removed a blank \"ol\" element.");
			} else if (text.startsWith("<ol><", p1)) {
//				text2 = text2.substring(0, text2.length() - numSpaces);
//				for (int i = 0; i < depth; i++) {
//					text2 += " ";
//				}
				text2 += "<ol>\n";
				p1 += 4;
				depth ++;
			} else if (text.startsWith("<ol>", p1)) {
//				text2 = text2.substring(0, text2.length() - numSpaces);
//				for (int i = 0; i < depth; i++) {
//					text2 += " ";
//				}
				text2 += "<ol>";
				p1 += 4;
				depth ++;
			} else if (text.startsWith("</ol>", p1)) {
				if (text2.endsWith(">")) {
					text2 += "\n";
				}
				
				depth --;
				text2 = text2.substring(0, text2.length() - numSpaces);
				for (int i = 0; i < depth; i++) {
					text2 += " ";
				}
				text2 += "</ol>";
				if (depth == 0) {
					text2 += "\n";
				}
				p1 += 5;
			} else if (text.startsWith("\n</li>", p1)) {
				p1 += 1;
			} else if (text.startsWith("<li>", p1)) {
				text2 = text2.substring(0, text2.length() - numSpaces);
				for (int i = 0; i < depth; i++) {
					text2 += " ";
				}
				text2 += "<li>";
				p1 += 4;
			} else if (text.startsWith("\n", p1)) {
				// Trims trailing spaces.
				if (numTrailingSpaces != 0) {
					text2 = text2.substring(0, text2.length() - numTrailingSpaces);
					System.out.println("Trimmed trailing spaces at: " + p1);
				}
				text2 += "\n";
				p1++;
			} else {
				text2 += text.substring(p1, p1 + 1);
				p1++;
			}
			
			if (text.startsWith(" ", p0)) {
				numSpaces++;
			} else {
				numSpaces = 0;
			}
			
			if (text.startsWith(" ", p0) ||
					text.startsWith("\t", p0) ||
					text.startsWith("　", p0) // Japanese "Zenkaku space".
					) {
				numTrailingSpaces++;
			} else {
				numTrailingSpaces = 0;
			}
		}
		text2 = text2.replaceAll("\n\n+", "\n\n");
		if (text2.endsWith("\n\n")) {
			text2 = text2.substring(0, text2.length() - 1);
		}
		while (text2.startsWith("\n")) {
			text2 = text2.substring(1, text2.length());
		}
		
		System.out.println("Number of <br>s removed: " + numBreakRemoved);
		int numAfter = text2.length();
		destination.setText(text2);
		System.out.println("Formatted. Num chars: " + numBefore + " -> " + numAfter);
		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {
				destination.select(0, 0);
			}
		});
	}
	
	private class MyTextArea extends JTextArea {
		public MyTextArea() {
			setLineWrap(true);
			setFont(new Font("monospaced", 0, 13));
		}
	}
	
	private class SourceTextArea extends MyTextArea { }
	
	private class DestinationTextArea extends MyTextArea { }
}
